# 1. 什么是类和实例并说明他们之间的关系
# 类：指具有相同特征的一类事物的集合，是抽象的，用计算机语言来描述类就是共同特征属性和方法的集合
# 实例：是类的具象，是一个实体
# 两者关系：类用于描述某一类实例的共同特征，而实例则是类的具体存在

# 2. 类的实例方法、类方法和静态分别如何定义举例说明，并总结它们的应用场景
# 类的实例方法：在类的实例化后，会自动绑定到类的每个实例中，一般用self来代表当前实例，最好通过实例来调用
class Person:
    def print_name(self):
        print("你的名字是{}".format(self))


p = Person()  # 实例化p，会将p自动传入self中,self = p
p.print_name()  # 你的名字是<__main__.Person object at 0x0000026812437B50>
print(p,
      p.print_name)  # <__main__.Person object at 0x000001E1F8AE5660> <bound method Person.print_name of <__main__.Person object at 0x0000017AB068EB60>>

p1 = Person()
p1.print_name()  # 你的名字是<__main__.Person object at 0x0000026812437880>
print(p1,
      p1.print_name)  # <__main__.Person object at 0x000001E1F8A9E860>,<bound method Person.print_name of <__main__.Person object at 0x0000025A03C7EE60>>


# 以上两个例子都可以看到方法绑定到了相应的实例对象上
# Person.print_name() TypeError: Person.print_name() missing 1 required positional argument: 'self'
# 不能通过类来调用实例方法，通过类调用会把里面的self方法当前普通的函数调用，缺少参数注入，当然也可以手动传入p，Person.print_name(p)，但是没有绑定效果


# 类方法：类方法就是绑定到类本身上，既可以访问类属性，也可以访问实例属性
# 使用@classmethod装饰器，
# 类方法装饰器装饰过的方法，通过实例对象A.__class__找到当前类，或者类访问该方法时，都会绑定到当前类，调用时，会自动注入第一参数为当前类
# 类方法一般第一个参数为cls
class A:
    @classmethod  # test=classmethod(test)
    def test(cls):
        print("test~~~~", id(cls), type(cls))


# 通过实例来调用，可以看到此时cls的类型模版是type，如果是将实例x传入到了cls中，cls的类模版应该是A，但此时是type，说明是将类A传入到了cls中
# 类方法：原来是自动将实例注入到cls中，使用@classmethod装饰器后将实例拦截，通过x.__class__字典找到他的类A，将实例x换成类A传入到cls中

x = A()
print(x.__class__)  # <class '__main__.A'>
x.test()  # test~~~~ 1973130725040 <class 'type'>

# 通过类来直接调用，就是可以看到是将类A传入到了类方法的self中，上下两个地址也相同
A.test()  # test~~~~ 1973130725040 <class 'type'>

# 都可以看到该方法绑定到了类中
print(A.test)  # <bound method A.test of <class '__main__.A'>>
print(x.test)  # <bound method A.test of <class '__main__.A'>>


# 静态方法：
#     是与类关联的函数，并没有和类的实例或类本身关联。
#     静态方法不绑定到任何实例或类，它们只是与类相关联的函数。静态方法不能访问实例或类的属性。
class B:
    age = 18

    def __init__(self, name):
        self.name = name  # 实例属性

    @staticmethod
    def print_name(name):  # 静态方法
        # print(age) 无法调用类的属性
        print("Hello, {}".format(name))


# 类直接调用
B.print_name("tom")  # Hello, tom

# 通过实例调用，需要传入实参，实例的属性无法调用
x = B("amd")
x.print_name("amd")  # 需要再次传入参数，不能调用x.name实例属性

# 静态方法，它不绑定到任何实例或类,没有任何绑定关系，就是在类中的普通函数
print(B.print_name)  # <function B.print_name at 0x000001CB42C71A20>
print(x.print_name)  # <function B.print_name at 0x000001CB42C71A20>

# 应用场景：
# 如果要封装一个工具类，对参数没有要求，则可以使用静态函数，如果有要求，则可以使用类函数
# 在实际项目开发中，使用实例方法较多


# 3. MRO是什么，描述其查找顺序
# Python MRO（方法解析顺序）是一种确定类中方法调用顺序的算法。用于解决多继承中方法名冲突覆盖的问题，确定了方法的搜索顺序，解决继承中方法调用的问题。
# MRO使用C3算法来确定方法的搜索顺序。搜索顺序是根据类的继承关系来确定的，遵循以下规则：
# 深度优先搜索：从当前类开始，按照继承顺序，从左到右搜索，遍历继承类
# 如果有多个父类，在每个类中只会被查找一次，如果父类相同，则以排在后边查找到的为准
# 当多个父类拥有相同的方法时，选择排在前面的父类的方法。


# 4. Mixin是什么，描述其应用场景
# Mixin将多个类的方法和属性组合到一起，创建更复杂、更灵活的类结构，体现的是一种组合的设计模式
# Mixin本身是一个类，可以包含方法和属性，并且可以与其他类进行混入。
# Mixin的应用场景包括：
# 代码重用：通过将具有相似功能的方法和属性组合到一起，通过多继承的方式让多个类可以共享Mixin中的方法和属性，而不需要重复相同的代码。
# 功能扩展：我们可能需要向现有的类添加新的方法和属性，而又不改变原始类的代码。Mixin可以用于扩展现有类的功能，而不需要直接修改原始类的代码。


# 5. 根据本周异常相关视频，总结try/except/else/finally的用法及如何自定义异常

# try/ except / else / finally
# try: 用于定义可能抛出异常的代码块。
# except: 用于捕获并处理异常。当try代码块中抛出异常时，程序会跳转到匹配的except代码块进行处理。
# else: 可以在try / except语句之后使用，用于处理没有抛出异常的try代码块。
# finally: 用于定义无论是否发生异常都需要执行的代码块。

try:
    # 可能抛出异常的代码
    x = 1 / 0
except ZeroDivisionError as e:
    # 处理除零异常
    print("除零错误")
else:
    # 没有异常发生，执行else代码块
    print("没有异常")
finally:
    # 无论是否发生异常，都会执行finally代码
    print("不管是否有异常，都需要执行")


# 自定义异常
#   创建一个自定义异常，创建一个新的类，并继承内置的Exception类，再使用raise语句抛出自定义的异常。

class MyException(Exception):
    def __init__(self, mes):
        self.message = mes
        super().__init__(mes)
    def __str__(self):
        return self.message

try:
    raise MyException("自定义异常")
except MyException as e:
    print(e.message)
